﻿//
// CommandLineInterpreter.cs
//
// Authors:
//  Kenny York <kennyyork@gmail.com>
//
// Copyright (C) 2009 Kenny York
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
// 
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
// 
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace clidotnet
{
    /*
     * Command names can be reused for multiple parameters, but not for groups.  For example, if you have a command named 'print',
     * you cannot have a group named 'print' or visa versa.  This only applies at the currently tree level.  So you can have a
     * 'print' command at the root and a 'group1 print' command.
     *
     * CommandLineInterpreter can be created manually by adding Command or CommandGroup objects
     * or by using the collection initializer syntax.
     * 
     * Example - Collection Initializer:
     * 
     * CommandLineInterpreter cli = new CommandLineInterpreter()
     * {
     *      {"print", "print Hello, World!", PrintHello },
     *      {"print", "print a message", (string s) => { Console.WriteLine(s); } },
     *      {"print", "print a message x times", (string s, int x) => { for(int i=0;i<x;++i) Console.WriteLine(s); } },
     * };
     * 
     * The first argument is the name, the second is the help text (optional), and the third is the lamba or delegate to call.
     * 
     * 
     * Example - Adding Objects
     * 
     * CommandLineInterpreter cli = new CommandLineInterpreter();
     * cli.Add(new Command("print", () => { Console.WriteLine("Hello, World!"); }));
     * cli.Add(new Command<string>("print", (string s) => { Console.WriteLine(s); }));  
     * 
     * 
     */
    public class CommandLineInterpreter : System.Collections.IEnumerable, ICommandInitializer
    {
        private class HelpCommand : CommandBase
        {
            private CommandGroup baseGroup;
            private CommandGroup cliGroup;

            public HelpCommand(CommandGroup baseGroup, CommandGroup cliGroup)
                : base("help|?", "Print help information for a command")
            {
                this.baseGroup = baseGroup;
                this.cliGroup = cliGroup;
            }

            public override int ArgCount
            {
                get { throw new NotImplementedException(); }
            }

            protected override void Run(List<string> args)
            {
                if (args == null)
                {
                    return;
                }

                if (args.Count == 0 || (args.Count == 1 && Implements(args[0])))
                {
                    Console.WriteLine(baseGroup.ToString(false));
                    Console.WriteLine(cliGroup.ToString(false));
                }
                else
                {
                    args.RemoveAt(0);
                    List<string> argCopy = new List<string>(args);
                    if (!DigDown(baseGroup, argCopy))
                    {
                        DigDown(cliGroup, args);
                    }
                }
            }

            private bool DigDown(CommandGroup cGroup, List<string> args)
            {
                if (args.Count == 1)
                {
                    //looking for command or group to print
                    var cmds = from c in cGroup where (c.Implements(args[0])) select c;
                    if (cmds.Count() > 0)
                    {
                        foreach (ICommand c in cmds)
                        {
                            if (c is CommandGroup)
                            {
                                Console.WriteLine(((CommandGroup)c).ToString(false));
                            }
                            else
                            {
                                Console.WriteLine(c.ToString());
                            }
                        }
                        
                        return true;
                    }
                }
                else if( args.Count > 1)
                {
                    //looking for a group
                    var cmds = from c in cGroup where (c.Implements(args[0]) && (c is CommandGroup)) select c;
                    CommandGroup g = cmds.SingleOrDefault() as CommandGroup;
                    if (g != null)
                    {
                        args.RemoveAt(0);
                        DigDown(g, args);
                    }
                }

                return false;
            }

            public override bool Implements(string command, int argCount)
            {
                return names.Contains(command);
            }

            public override ICommand Implements(string command, List<string> args)
            {
                if( names.Contains(command) )
                {
                    return this;
                }
                return null;
            }
        }

        private CommandGroup baseGroup;
        private CommandGroup cliGroup;

        private bool exit = false;
        public CommandLineInterpreter()
            : base()
        {
            Init();
        }

        private CommandLineInterpreter(IEnumerable<ICommand> collection)
        {
            Init();
            
            foreach (ICommand c in collection)
            {
                ValidateCommand(c);
                baseGroup.Add(c);
            }
        }

        private void Init()
        {
            baseGroup = new CommandGroup("root");
            cliGroup = new CommandGroup("cli");
            cliGroup.Add(new HelpCommand(baseGroup,cliGroup));
            cliGroup.Add("quit|q",Exit);
            cliGroup.Add("clear","clear the console",Clear);            
        }

        private void Help()
        {
            
        }

        private void Exit()
        {
            exit = true;
        }

        private void Clear()
        {
            Console.Clear();
        }

        /// <summary>
        /// Enter the Read-Evaluate-Print Loop
        /// </summary>
        public void Repl()
        {
            while (!exit)
            {                            
                Console.Write("> ");
                string line = Console.ReadLine();
                try
                {
                    Parse(line);                    
                }
                catch (Exception ex)
                {
                    Console.WriteLine("Parse Exception: {0}",ex.Message);                
                }
            }

            exit = false;
        }

        /// <summary>
        /// Enter the Read-Evaluate-Print Loop, and immediately run the provided commands
        /// </summary>
        /// <param name="args">An array of commands to run</param>
        public void Repl(IEnumerable<string> args)
        {            
            foreach (string s in args)
            {
                Console.WriteLine("> {0}", s);
                Parse(s);                
            }

            Repl();
        }

        /// <summary>
        /// Parse the provided string into tokens.
        /// </summary>
        /// <param name="command">Command to parse</param>
        /// <returns>A List&ltstring&gt of tokens</returns>
        private List<string> GetArgs(string command)
        {
            command = command.Trim();
            List<string> args = new List<string>();

            bool scanningWord = true;
            bool quoteOpen = false;            
            int startOfWord = 0;

            bool lookingForWhitespace = true;           

            for (int i = 0; i < command.Length; ++i)
            {
                //if a \ is found, remove it, and progress the index past it and the following character
                if (command[i] == '\\')
                {
                    command = command.Remove(i, 1);                           
                    continue;
                }

                //if we are in a quoted string and we find a quote, grab the string
                if (quoteOpen && command[i] == '"')
                {
                    quoteOpen = false;
                    args.Add(command.Substring(startOfWord, i - startOfWord));
                    lookingForWhitespace = false;
                }
                else if (command[i] == '"' && !lookingForWhitespace)
                {
                    quoteOpen = true;
                    startOfWord = i + 1;
                }
                else if(!quoteOpen)
                {
                    if (lookingForWhitespace && char.IsWhiteSpace(command[i]))
                    {
                        args.Add(command.Substring(startOfWord, i - startOfWord));
                        lookingForWhitespace = false;
                    }
                    if (!lookingForWhitespace && !char.IsWhiteSpace(command[i]))
                    {
                        lookingForWhitespace = true;
                        startOfWord = i;
                    }

                    //end of string, grab word
                    if (i == (command.Length - 1) && scanningWord)
                    {
                        args.Add(command.Substring(startOfWord, (i + 1) - startOfWord));
                    }
                }
            }

            if (quoteOpen)
            {
                throw new ArgumentException("Open quote without matching closing quote.");
            }

            return args;
        }

        public void Parse(string input)
        {
            List<string> args;

            //an empty string triggers the help to print
            if (string.IsNullOrEmpty(input))
            {
                args = new List<string>();
            }
            else
            {
                try
                {
                    args = GetArgs(input);
                    if( args.Count == 0 )
                    {
                        args.Add("help");
                    }
                }
                catch (Exception ex)
                {
                    throw ex;
                }
            }

            //run the command through our cliCommands
            try
            {                
                List<string> argsCopy = new List<string>(args);
                ICommand c = baseGroup.Implements("root", argsCopy);
                
                if (c == null)
                {
                    argsCopy = new List<string>(args);
                    c = cliGroup.Implements("cli", argsCopy);
                }

                if (c != null)
                {
                    c.Execute(argsCopy);
                }
                else
                {
                    Console.WriteLine("Unknown Command \"{0}\" that accepts {1} parameters.", args[0], (args.Count - 1));
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }          
        }        

        //public void PrintCommandTree()
        //{
        //    ConsoleColor color = Console.ForegroundColor;
        //    DrillDown(this,0);
        //    Console.ForegroundColor = color;
        //}

        //private void DrillDown(CommandList commands, int depth)
        //{
        //    foreach (ICommand c in commands)
        //    {
        //        if (c is CommandGroup)
        //        {
        //            Console.ForegroundColor = ConsoleColor.Yellow;
        //        }
        //        else
        //        {
        //            Console.ForegroundColor = ConsoleColor.Green;
        //        }

        //        for (int i = 0; i < depth; ++i)
        //        {
        //            Console.Write("| ");
        //        }

        //        Console.Write("|-");
                
        //        Console.WriteLine(c.Name);
                
        //        if (c is CommandGroup)
        //        {
        //            depth += 1;
        //            DrillDown((CommandList)c, depth);
        //            depth -= 1;
        //        }
        //    }
        //}

        public void ValidateCommand(ICommand command)
        {
            if (command == null)
            {
                throw new ArgumentNullException();
            }

            //ICommand impl = cliGroup.Implements(command.Name,

            if (cliGroup.Contains(command))
            {
                throw new DuplicateCommandException(command);
            }
        }

        #region ICommandInitializer Members

        /// <summary>
        /// Adds a CommandGroup to the list of ICommand objects
        /// </summary>
        /// <param name="group">The CommandGroup to add</param>
        /// <exception cref="ArgumentNullException">Thrown when group is null.</exception>
        /// <exception cref="ArgumentException">Thrown when the named given by group is already in use.</exception>
        public void Add(CommandGroup group)
        {
            baseGroup.Add(group);
        }

        /// <summary>
        /// Add a Command with no parameters
        /// </summary>
        /// <param name="name">Pipe (|) delimited list of names used to call the command</param>
        /// <param name="action">The action to perform when <pararef name="name"/> is called.</param>
        public void Add(string name, CommandLineAction action)
        {
            ICommand command = new Command(name, action);

            ValidateCommand(command);

            baseGroup.Add(command);
        }        

        /// <summary>
        /// Add a Command with no parameters
        /// </summary>
        /// <param name="name">Pipe (|) delimited list of names used to call the command</param>
        /// <param name="help">Help text (optional)</param>
        /// <param name="action">The action to perform when <pararef name="name"/> is called.</param>
        public void Add(string name, string help, CommandLineAction action)
        {
            ICommand command = new Command(name, help, action);

            ValidateCommand(command);
            
            baseGroup.Add(command);
        }

        /// <summary>
        /// Add a Command to the CommandList
        /// </summary>
        /// <param name="command"></param>
        public void Add(Command command)
        {
            ValidateCommand(command);

            baseGroup.Add(command);
        }

        /// <summary>
        /// Add a Command with one parameter
        /// </summary>
        /// <param name="name">Pipe (|) delimited list of names used to call the command</param>
        /// <param name="action">The action to perform when <pararef name="name"/> is called.</param>
        public void Add<T>(string name, CommandLineAction<T> action)
        {
            ICommand command = new Command<T>(name, action);

            ValidateCommand(command);

            baseGroup.Add(command);
        }

        /// <summary>
        /// Add a Command with one parameter
        /// </summary>
        /// <param name="name">Pipe (|) delimited list of names used to call the command</param>
        /// <param name="help">Help text (optional)</param>
        /// <param name="action">The action to perform when <pararef name="name"/> is called.</param>
        public void Add<T>(string name, string help, CommandLineAction<T> action)
        {
            ICommand command = new Command<T>(name, help, action);

            ValidateCommand(command);

            baseGroup.Add(command);
        }

        /// <summary>
        /// Add a Command with one parameter
        /// </summary>
        /// <param name="command"></param>
        public void Add<T>(Command<T> command)
        {
            ValidateCommand(command);

            baseGroup.Add(command);
        }

        /// <summary>
        /// Add a Command with two parameters
        /// </summary>
        /// <param name="name">Pipe (|) delimited list of names used to call the command</param>
        /// <param name="action">The action to perform when <pararef name="name"/> is called.</param>
        public void Add<T0, T1>(string name, CommandLineAction<T0, T1> action)
        {
            ICommand command = new Command<T0, T1>(name, action);

            ValidateCommand(command);

            baseGroup.Add(command);
        }

        /// <summary>
        /// Add a Command with two parameters
        /// </summary>
        /// <param name="name">Pipe (|) delimited list of names used to call the command</param>
        /// <param name="help">Help text (optional)</param>
        /// <param name="action">The action to perform when <pararef name="name"/> is called.</param>
        public void Add<T0, T1>(string name, string help, CommandLineAction<T0, T1> action)
        {
            ICommand command = new Command<T0, T1>(name, help, action);

            ValidateCommand(command);

            baseGroup.Add(command);
        }

        /// <summary>
        /// Add a Command with two parameters
        /// </summary>
        /// <param name="command"></param>
        public void Add<T0, T1>(Command<T0, T1> command)
        {
            ValidateCommand(command);

            baseGroup.Add(command);
        }

        /// <summary>
        /// Add a Command with three parameters
        /// </summary>
        /// <param name="name">Pipe (|) delimited list of names used to call the command</param>
        /// <param name="action">The action to perform when <pararef name="name"/> is called.</param>
        public void Add<T0, T1, T2>(string name, CommandLineAction<T0, T1, T2> action)
        {
            ICommand command = new Command<T0, T1, T2>(name, action);

            ValidateCommand(command);

            baseGroup.Add(command);
        }

        /// <summary>
        /// Add a Command with three parameters
        /// </summary>
        /// <param name="name">Pipe (|) delimited list of names used to call the command</param>
        /// <param name="help">Help text (optional)</param>
        /// <param name="action">The action to perform when <pararef name="name"/> is called.</param>
        public void Add<T0, T1, T2>(string name, string help, CommandLineAction<T0, T1, T2> action)
        {
            ICommand command = new Command<T0, T1, T2>(name, help, action);

            ValidateCommand(command);

            baseGroup.Add(command);
        }

        /// <summary>
        /// Add a Command with three parameters
        /// </summary>
        /// <param name="command"></param>
        public void Add<T0, T1, T2>(Command<T0, T1, T2> command)
        {
            ValidateCommand(command);

            baseGroup.Add(command);
        }

        /// <summary>
        /// Add a Command with four parameters
        /// </summary>
        /// <param name="name">Pipe (|) delimited list of names used to call the command</param>
        /// <param name="action">The action to perform when <pararef name="name"/> is called.</param>
        public void Add<T0, T1, T2, T3>(string name, CommandLineAction<T0, T1, T2, T3> action)
        {
            ICommand command = new Command<T0, T1, T2, T3>(name, action);

            ValidateCommand(command);

            baseGroup.Add(command);
        }

        /// <summary>
        /// Add a Command with four parameters
        /// </summary>
        /// <param name="name">Pipe (|) delimited list of names used to call the command</param>
        /// <param name="help">Help text (optional)</param>
        /// <param name="action">The action to perform when <pararef name="name"/> is called.</param>
        public void Add<T0, T1, T2, T3>(string name, string help, CommandLineAction<T0, T1, T2, T3> action)
        {
            ICommand command = new Command<T0, T1, T2, T3>(name, help, action);

            ValidateCommand(command);

            baseGroup.Add(command);
        }

        /// <summary>
        /// Add a Command with four parameters
        /// </summary>
        /// <param name="command"></param>
        public void Add<T0, T1, T2, T3>(Command<T0, T1, T2, T3> command)
        {
            ValidateCommand(command);

            baseGroup.Add(command);
        }

        /// <summary>
        /// Add a Command with five parameters
        /// </summary>
        /// <param name="name">Pipe (|) delimited list of names used to call the command</param>
        /// <param name="action">The action to perform when <pararef name="name"/> is called.</param>
        public void Add<T0, T1, T2, T3, T4>(string name, CommandLineAction<T0, T1, T2, T3, T4> action)
        {
            ICommand command = new Command<T0, T1, T2, T3, T4>(name, action);

            ValidateCommand(command);

            baseGroup.Add(command);
        }

        /// <summary>
        /// Add a Command with five parameters
        /// </summary>
        /// <param name="name">Pipe (|) delimited list of names used to call the command</param>
        /// <param name="help">Help text (optional)</param>
        /// <param name="action">The action to perform when <pararef name="name"/> is called.</param>
        public void Add<T0, T1, T2, T3, T4>(string name, string help, CommandLineAction<T0, T1, T2, T3, T4> action)
        {
            ICommand command = new Command<T0, T1, T2, T3, T4>(name, help, action);

            ValidateCommand(command);

            baseGroup.Add(command);
        }

        /// <summary>
        /// Add a Command with five parameters
        /// </summary>
        /// <param name="command"></param>
        public void Add<T0, T1, T2, T3, T4>(Command<T0, T1, T2, T3, T4> command)
        {
            ValidateCommand(command);

            baseGroup.Add(command);
        }

        /// <summary>
        /// Add a Command with six parameters
        /// </summary>
        /// <param name="name">Pipe (|) delimited list of names used to call the command</param>
        /// <param name="action">The action to perform when <pararef name="name"/> is called.</param>
        public void Add<T0, T1, T2, T3, T4, T5>(string name, CommandLineAction<T0, T1, T2, T3, T4, T5> action)
        {
            ICommand command = new Command<T0, T1, T2, T3, T4, T5>(name, action);

            ValidateCommand(command);

            baseGroup.Add(command);
        }

        /// <summary>
        /// Add a Command with six parameters
        /// </summary>
        /// <param name="name">Pipe (|) delimited list of names used to call the command</param>
        /// <param name="help">Help text (optional)</param>
        /// <param name="action">The action to perform when <pararef name="name"/> is called.</param>
        public void Add<T0, T1, T2, T3, T4, T5>(string name, string help, CommandLineAction<T0, T1, T2, T3, T4, T5> action)
        {
            ICommand command = new Command<T0, T1, T2, T3, T4, T5>(name, help, action);

            ValidateCommand(command);

            baseGroup.Add(command);
        }

        /// <summary>
        /// Add a Command with six parameters
        /// </summary>
        /// <param name="command"></param>
        public void Add<T0, T1, T2, T3, T4, T5>(Command<T0, T1, T2, T3, T4, T5> command)
        {
            ValidateCommand(command);

            baseGroup.Add(command);
        }

        /// <summary>
        /// Add a Command with seven parameters
        /// </summary>
        /// <param name="name">Pipe (|) delimited list of names used to call the command</param>
        /// <param name="action">The action to perform when <pararef name="name"/> is called.</param>
        public void Add<T0, T1, T2, T3, T4, T5, T6>(string name, CommandLineAction<T0, T1, T2, T3, T4, T5, T6> action)
        {
            ICommand command = new Command<T0, T1, T2, T3, T4, T5, T6>(name, action);

            ValidateCommand(command);

            baseGroup.Add(command);
        }

        /// <summary>
        /// Add a Command with seven parameters
        /// </summary>
        /// <param name="name">Pipe (|) delimited list of names used to call the command</param>
        /// <param name="help">Help text (optional)</param>
        /// <param name="action">The action to perform when <pararef name="name"/> is called.</param>
        public void Add<T0, T1, T2, T3, T4, T5, T6>(string name, string help, CommandLineAction<T0, T1, T2, T3, T4, T5, T6> action)
        {
            ICommand command = new Command<T0, T1, T2, T3, T4, T5, T6>(name, help, action);

            ValidateCommand(command);

            baseGroup.Add(command);
        }

        /// <summary>
        /// Add a Command with seven parameters
        /// </summary>
        /// <param name="command"></param>
        public void Add<T0, T1, T2, T3, T4, T5, T6>(Command<T0, T1, T2, T3, T4, T5, T6> command)
        {
            ValidateCommand(command);

            baseGroup.Add(command);
        }

        /// <summary>
        /// Add a Command with eight parameters
        /// </summary>
        /// <param name="name">Pipe (|) delimited list of names used to call the command</param>
        /// <param name="action">The action to perform when <pararef name="name"/> is called.</param>
        public void Add<T0, T1, T2, T3, T4, T5, T6, T7>(string name, CommandLineAction<T0, T1, T2, T3, T4, T5, T6, T7> action)
        {
            ICommand command = new Command<T0, T1, T2, T3, T4, T5, T6, T7>(name, action);

            ValidateCommand(command);

            baseGroup.Add(command);
        }

        /// <summary>
        /// Add a Command with eight parameters
        /// </summary>
        /// <param name="name">Pipe (|) delimited list of names used to call the command</param>
        /// <param name="help">Help text (optional)</param>
        /// <param name="action">The action to perform when <pararef name="name"/> is called.</param>
        public void Add<T0, T1, T2, T3, T4, T5, T6, T7>(string name, string help, CommandLineAction<T0, T1, T2, T3, T4, T5, T6, T7> action)
        {
            ICommand command = new Command<T0, T1, T2, T3, T4, T5, T6, T7>(name, help, action);

            ValidateCommand(command);

            baseGroup.Add(command);
        }

        /// <summary>
        /// Add a Command with eight parameters
        /// </summary>
        /// <param name="command"></param>
        public void Add<T0, T1, T2, T3, T4, T5, T6, T7>(Command<T0, T1, T2, T3, T4, T5, T6, T7> command)
        {
            ValidateCommand(command);

            baseGroup.Add(command);
        }

        /// <summary>
        /// Add a Command with nine parameters
        /// </summary>
        /// <param name="name">Pipe (|) delimited list of names used to call the command</param>
        /// <param name="action">The action to perform when <pararef name="name"/> is called.</param>
        public void Add<T0, T1, T2, T3, T4, T5, T6, T7, T8>(string name, CommandLineAction<T0, T1, T2, T3, T4, T5, T6, T7, T8> action)
        {
            ICommand command = new Command<T0, T1, T2, T3, T4, T5, T6, T7,T8>(name, action);

            ValidateCommand(command);

            baseGroup.Add(command);
        }

        /// <summary>
        /// Add a Command with nine parameters
        /// </summary>
        /// <param name="name">Pipe (|) delimited list of names used to call the command</param>
        /// <param name="help">Help text (optional)</param>
        /// <param name="action">The action to perform when <pararef name="name"/> is called.</param>
        public void Add<T0, T1, T2, T3, T4, T5, T6, T7, T8>(string name, string help, CommandLineAction<T0, T1, T2, T3, T4, T5, T6, T7, T8> action)
        {
            ICommand command = new Command<T0, T1, T2, T3, T4, T5, T6, T7,T8>(name, help, action);

            ValidateCommand(command);

            baseGroup.Add(command);
        }

        /// <summary>
        /// Add a Command with nine parameters
        /// </summary>
        /// <param name="command"></param>
        public void Add<T0, T1, T2, T3, T4, T5, T6, T7, T8>(Command<T0, T1, T2, T3, T4, T5, T6, T7, T8> command)
        {
            ValidateCommand(command);

            baseGroup.Add(command);
        }

        /// <summary>
        /// Add a Command with ten parameters
        /// </summary>
        /// <param name="name">Pipe (|) delimited list of names used to call the command</param>
        /// <param name="action">The action to perform when <pararef name="name"/> is called.</param>
        public void Add<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9>(string name, CommandLineAction<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9> action)
        {
            ICommand command = new Command<T0, T1, T2, T3, T4, T5, T6, T7,T8, T9>(name, action);

            ValidateCommand(command);

            baseGroup.Add(command);
        }

        /// <summary>
        /// Add a Command with ten parameters
        /// </summary>
        /// <param name="name">Pipe (|) delimited list of names used to call the command</param>
        /// <param name="help">Help text (optional)</param>
        /// <param name="action">The action to perform when <pararef name="name"/> is called.</param>
        public void Add<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9>(string name, string help, CommandLineAction<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9> action)
        {
            ICommand command = new Command<T0, T1, T2, T3, T4, T5, T6, T7,T8, T9>(name, help, action);

            ValidateCommand(command);

            baseGroup.Add(command);
        }

        /// <summary>
        /// Add a Command with ten parameters
        /// </summary>
        /// <param name="command"></param>
        public void Add<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9>(Command<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9> command)
        {
            ValidateCommand(command);

            baseGroup.Add(command);
        }

        #endregion

        #region IEnumerable Members

        public System.Collections.IEnumerator GetEnumerator()
        {
            return (System.Collections.IEnumerator)new VoidEnumerator();
        }

        public class VoidEnumerator : System.Collections.IEnumerator
        {

            #region IEnumerator Members

            public object Current
            {
                get { return null; }
            }

            public bool MoveNext()
            {
                return false;
            }

            public void Reset()
            {
                
            }

            #endregion
        }

        #endregion
    }
}
